d7e930b71ad2e14aacf33983572317a8ae36a3f4,src/org/exist/storage/recovery/RecoveryManager.java,RecoveryManager,doRecovery,#File#JournalReader#number#,169
Before Change
* @throws LogException
*/
private void doRecovery(File last, JournalReader reader, long lastLsn) throws LogException {
if (LOG.isDebugEnabled())
LOG.debug("Running recovery ...");
logManager.setInRecovery(true);
// map to track running transactions
After Change
*/
private void doRecovery(int txnCount, File last, JournalReader reader, long lastLsn) throws LogException {
if (LOG.isInfoEnabled())
LOG.info("Running recovery ...");
logManager.setInRecovery(true);
try {
// map to track running transactions
Long2ObjectHashMap runningTxns = new Long2ObjectHashMap();
// ------- REDO ---------
if (LOG.isInfoEnabled())
LOG.info("First pass: redoing " + txnCount + " transactions...");
ProgressBar progress = new ProgressBar("Redo ", last.length());
Loggable next = null;
int redoCnt = 0;
try {
while ((next = reader.nextEntry()) != null) {
SanityCheck.ASSERT(next.getLogType() != LogEntryTypes.CHECKPOINT,
"Found a checkpoint during recovery run! This should not ever happen.");
if (next.getLogType() == LogEntryTypes.TXN_START) {
// new transaction starts: add it to the transactions table
runningTxns.put(next.getTransactionId(), next);
} else if (next.getLogType() == LogEntryTypes.TXN_COMMIT) {
// transaction committed: remove it from the transactions table
runningTxns.remove(next.getTransactionId());
redoCnt++;
} else if (next.getLogType() == LogEntryTypes.TXN_ABORT) {
// transaction aborted: remove it from the transactions table
runningTxns.remove(next.getTransactionId());
}
// LOG.debug("Redo: " + next.dump());
// redo the log entry
next.redo();
progress.set(Lsn.getOffset(next.getLsn()));
if (next.getLsn() == lastLsn)
break; // last readable entry reached. Stop here.
}
} catch (Exception e) {
LOG.warn("Exception caught while redoing transactions. Aborting recovery.", e);
if (next != null)
LOG.warn("Log entry that caused the exception: " + next.dump());
throw new LogException("Recovery aborted");
} finally {
LOG.info("Redo processed " + redoCnt + " out of " + txnCount + " transactions.");
}
// ------- UNDO ---------
if (LOG.isInfoEnabled())
LOG.info("Second pass: undoing dirty transactions. Uncommitted transactions: " +
runningTxns.size());
// see if there are uncommitted transactions pending
if (runningTxns.size() > 0) {
// do a reverse scan of the log, undoing all uncommitted transactions
try {
while((next = reader.previousEntry()) != null) {
if (next.getLogType() == LogEntryTypes.TXN_START) {
if (runningTxns.get(next.getTransactionId()) != null) {
runningTxns.remove(next.getTransactionId());
if (runningTxns.size() == 0)
// all dirty transactions undone
break;
}
} else if (next.getLogType() == LogEntryTypes.TXN_COMMIT) {
// ignore already committed transaction
} else if (next.getLogType() == LogEntryTypes.CHECKPOINT) {
// found last checkpoint: undo is completed
break;
}
// undo the log entry if it belongs to an uncommitted transaction
if (runningTxns.get(next.getTransactionId()) != null) {
// LOG.debug("Undo: " + next.dump());
next.undo();
}
}
} catch (Exception e) {
LOG.warn("Exception caught while undoing dirty transactions. Remaining transactions " +
"to be undone: " + runningTxns.size(), e);
if (next != null)
LOG.warn("Log entry that caused the exception: " + next.dump());
throw new LogException("Recovery aborted");
}
}